package com.tsd.job;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.tsd.core.datasource.DataSourceContextHolder;
import com.tsd.core.thread.ThreadPoolTaskUtil;
import com.tsd.core.utils.ExceptionUtil;
import com.tsd.core.utils.HlpUtils;
import com.tsd.core.utils.ListUtil;
import com.tsd.job.dao.SysTaskJobMapper;
import com.tsd.job.entity.SysTaskJobExt;
import com.tsd.job.service.SysTaskJobService;
import com.tsd.utils.AutoInjectUtils;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.Trigger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
import org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.scheduling.quartz.SimpleTriggerFactoryBean;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * @author Hillpool
 */
@Component
@Order
public class TaskInitService implements BeanFactoryAware, CommandLineRunner {

    private static final Logger LOGGER = LoggerFactory.getLogger("quartzLogger");

    @Resource
    private SchedulerFactoryBean schedulerFactoryBean;
    @Resource
    private SysTaskJobService sysTaskJobService;
    @Resource
    private SysTaskJobMapper sysTaskJobMapper;

    private BeanFactory beanFactory;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    @Override
    public void run(String... args) {
        LOGGER.info("初始化定时任务");
        this.init();
    }

    /**
     * 初始化
     */
    public void init() {
        DataSourceContextHolder.setDataSourceType(DataSourceContextHolder.DEFAULT_ACCOUNT_SET_SID);
        syncTaskJob2Db();
        // 初始化任务列表
        try {
            List<SysTaskJobExt> jobList = sysTaskJobService.findAllEnable();
            if (!HlpUtils.isEmptyList(jobList)) {
                DefaultListableBeanFactory listableBeanFactory = (DefaultListableBeanFactory) beanFactory;
                schedulerFactoryBean.stop();
                List<Trigger> triggers = new ArrayList<>();
                for (SysTaskJobExt jobExt : jobList) {
                    try {
                        MethodInvokingJobDetailFactoryBean detailFactoryBean = new MethodInvokingJobDetailFactoryBean();
                        detailFactoryBean.setBeanFactory(beanFactory);
                        detailFactoryBean.setBeanName(jobExt.getJobBeanName());
                        detailFactoryBean.setConcurrent(true);
                        detailFactoryBean.setTargetBeanName(jobExt.getJob_object_name());
                        detailFactoryBean.setTargetMethod(jobExt.getJob_method_name());
                        detailFactoryBean.setArguments(jobExt);
                        detailFactoryBean.afterPropertiesSet();
                        listableBeanFactory.registerSingleton(jobExt.getJobBeanName(), detailFactoryBean);
                        JobDetail jobDetail = Objects.requireNonNull(detailFactoryBean.getObject());
                        if (SysTaskJobExt.TASK_TYPE_CRON.equals(jobExt.getTask_type())) {
                            CronTriggerFactoryBean trigger = new CronTriggerFactoryBean();
                            trigger.setBeanName(jobExt.getTriggerBeanName());
                            trigger.setJobDetail(jobDetail);
                            trigger.setCronExpression(jobExt.getCron_clause());
                            trigger.afterPropertiesSet();
                            triggers.add(trigger.getObject());
                        } else if (SysTaskJobExt.TASK_TYPE_SIMPLE.equals(jobExt.getTask_type())) {
                            SimpleTriggerFactoryBean trigger = new SimpleTriggerFactoryBean();
                            trigger.setBeanName(jobExt.getTriggerBeanName());
                            trigger.setJobDetail(jobDetail);
                            trigger.setRepeatInterval(jobExt.getInterval_time());
                            trigger.afterPropertiesSet();
                            triggers.add(trigger.getObject());
                        }
                    } catch (Exception e) {
                        LOGGER.error(ExceptionUtil.getStackTraceFromException(e));
                    }
                    Trigger[] tr = triggers.toArray(new Trigger[0]);
                    schedulerFactoryBean.setTriggers(tr);
                }
                schedulerFactoryBean.afterPropertiesSet();
                schedulerFactoryBean.start();
//                ContextFinalizer.setSchedulerFactoryBean(schedulerFactoryBean);
            }
        } catch (Exception e) {
            LOGGER.error("init cron tasks error," + e.getMessage(), e);
        }
    }

    /**
     * 同步代码中的定时任务设置到数据库
     */
    private void syncTaskJob2Db() {
        List<Object> list = sysTaskJobService.getJobObjectAndMethodList();
        if (HlpUtils.isEmptyList(list)) {
            return;
        }
        try {
            SysTaskJobExt params = new SysTaskJobExt();
            params.setDr(0);
            List<SysTaskJobExt> jobExtList = sysTaskJobService.queryByEntity(params);
            List<SysTaskJobExt> insertList = new ArrayList<>();
            List<SysTaskJobExt> updateList = new ArrayList<>();
            List<Long> existIdList = new ArrayList<>();
            for (Object object : list) {
                JSONObject jsonObject = (JSONObject) object;
                String object_name = jsonObject.getString("object_name");
                String object_desc = jsonObject.getString("object_desc");
                String mode = jsonObject.getString("mode");
                JSONArray methods = jsonObject.getJSONArray("methods");
                if (HlpUtils.isEmptyList(methods)) {
                    continue;
                }
                for (int i = 0; i < methods.size(); i++) {
                    try {
                        JSONObject obj = methods.getJSONObject(i);
                        String method_name = obj.getString("method_name");
                        String method_desc = obj.getString("method_desc");
                        SysTaskJobExt existJobExt = ListUtil.get(jobExtList, t -> t.getJob_object_name().equals(object_name)
                                && t.getJob_method_name().equals(method_name));
                        SysTaskJobExt jobExt = new SysTaskJobExt();
                        jobExt.setMode(mode);
                        jobExt.setJob_method_name(method_name);
                        jobExt.setJob_object_name(object_name);
                        jobExt.setName(object_desc + "-" + method_desc);
                        jobExt.setCode(method_name);
                        if (existJobExt != null) {
                            existIdList.add(existJobExt.getId());
                            jobExt.setId(existJobExt.getId());
                            AutoInjectUtils.autoInjectBaseData(jobExt, null, AutoInjectUtils.TYPE_UPDATE);
                            updateList.add(jobExt);
                        } else {
                            jobExt.setTask_type(SysTaskJobExt.TASK_TYPE_SIMPLE);
                            jobExt.setInterval_time(SysTaskJobExt.TASK_TYPE_SIMPLE_INTERVAL_MS);
                            jobExt.setState(SysTaskJobExt.TASK_STATE_DISABLE);
                            AutoInjectUtils.autoInjectBaseData(jobExt, null, AutoInjectUtils.TYPE_CREATE);
                            insertList.add(jobExt);
                        }
                    } catch (Exception e) {
                        LOGGER.error(ExceptionUtil.getStackTraceFromException(e));
                    }
                }
            }
            if (!HlpUtils.isEmptyList(insertList)) {
                ThreadPoolTaskUtil.executeInList4Split(insertList, false, itemList -> sysTaskJobMapper.insertList(itemList));
            }
            if (!HlpUtils.isEmptyList(updateList)) {
                ThreadPoolTaskUtil.executeInList4Split(updateList, false, itemList -> sysTaskJobMapper.updateList(itemList));
            }
            // 清除不存在的任务
            List<SysTaskJobExt> delList = ListUtil.filter(jobExtList, t -> !existIdList.contains(t.getId()));
            if (!HlpUtils.isEmptyList(delList)) {
                ThreadPoolTaskUtil.executeInList4Split(delList, false, itemList -> {
                    for (SysTaskJobExt jobExt : itemList) {
                        AutoInjectUtils.autoInjectBaseData(jobExt, null, AutoInjectUtils.TYPE_DELETE);
                    }
                    sysTaskJobMapper.updateList(itemList);
                });
            }
        } catch (Exception e) {
            LOGGER.error("同步代码中的定时任务设置到数据库失败：" + ExceptionUtil.getStackTraceFromException(e));
        }
    }

    private void init2() {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        if (scheduler == null) {
            LOGGER.error("初始化定时任务组件失败，Scheduler is null...");
            return;
        }

        // 初始化任务列表
        try {
            List<SysTaskJobExt> jobList = sysTaskJobService.findAllEnable();
            if (jobList != null) {
                for (SysTaskJobExt job : jobList) {
                    sysTaskJobService.updateQuartzFactory(job);
                }
            }
        } catch (Exception e) {
            LOGGER.error("init cron tasks error," + e.getMessage(), e);
        }

        try {
            LOGGER.info("The scheduler is starting...");
            scheduler.start(); // start the scheduler
        } catch (Exception e) {
            LOGGER.error("The scheduler start is error," + e.getMessage(), e);
        }
    }
}
